#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <TabLite.h>

extern unsigned int TabLiteBKDRHash(char *str);

static TabElem* __TabLiteGetByHash(TabLite* tab, unsigned long hashKey)
{
    TabElem* tmp;
    ForEachTabElem(tab, tmp)
    {
        if (tmp->tag != TAB_TAG_NIL)
        {
            if (tmp->hashKey == hashKey)
            {
                TabErrorSet(TAB_ERROR_OK);
                return tmp;
            }
        }
    }
    if (!tab->next)
    {
        TabErrorSet(TAB_ERROR_NOFOUND);
        return NULL;
    }
    return __TabLiteGetByHash(tab->next, hashKey);
}

TabElem* TabLiteGetByKey(TabLite* tab, char* name)
{
    if (!tab || !name || !strlen(name))
    {
        TabErrorSet(TAB_ERROR_ARG);
        return NULL;
    }
    
    unsigned long hashKey = TabLiteBKDRHash(name);
    return __TabLiteGetByHash(tab, hashKey);
}

TabElem *TabLiteGetByHash(TabLite* tab, unsigned long hash)
{
    if (!tab)
    {
        TabErrorSet(TAB_ERROR_ARG);
        return NULL;
    }
    return __TabLiteGetByHash(tab, hash);
}

static TabElem* __TabLiteGetByIndex(TabLite* tab, unsigned long index, unsigned long base_index)
{
    TabElem* tmp;
    ForEachTabElem(tab, tmp)
    {
        if (tmp->tag != TAB_TAG_NIL)
        {
            if (base_index == index)    // get the index
            {
                TabErrorSet(TAB_ERROR_OK);
                return tmp;
            }
        }
        base_index++;
    }
    if (base_index > index)
    {
        TabErrorSet(TAB_ERROR_NOFOUND);
        return NULL;
    }
    if (!tab->next)
    {
        TabErrorSet(TAB_ERROR_NOFOUND);
        return NULL;
    }
    return __TabLiteGetByIndex(tab->next, index, base_index);
}

TabElem* TabLiteGetByIndex(TabLite* tab, unsigned long index)
{
    if (!tab)
    {
        TabErrorSet(TAB_ERROR_ARG);
        return NULL;
    }
    return __TabLiteGetByIndex(tab, index, 0);
}

static TabElem *TabLiteGetFirst(TabLite* tab)
{
    TabElem* tmp;
    ForEachTabElem(tab, tmp)
    {
        if (tmp->tag != TAB_TAG_NIL)
        {
            TabErrorSet(TAB_ERROR_OK);
            return tmp;
        }
    }
    if (!tab->next)
    {
        TabErrorSet(TAB_ERROR_NOFOUND);
        return NULL;
    }
    return TabLiteGetFirst(tab->next);
}

static TabElem *TabLiteGetNext(TabLite* tab, TabElem *next)
{
    TabElem* tmp;
    ForEachTabElem(tab, tmp)
    {
        if (tmp->tag != TAB_TAG_NIL && tmp > next)
        {
            TabErrorSet(TAB_ERROR_OK);
            return tmp;
        }
    }
    if (!tab->next)
    {
        TabErrorSet(TAB_ERROR_NOFOUND);
        return NULL;
    }
    return TabLiteGetNext(tab->next, next);
}

TabElem *TabLiteGetByNext(TabLite* tab, TabElem *next)
{
    if (!tab)
    {
        TabErrorSet(TAB_ERROR_ARG);
        return NULL;
    }
    if (!next)  /* first elem */
    {
        return TabLiteGetFirst(tab);
    }
    return TabLiteGetNext(tab, next); 
}
